/*
 *  Copyright 2007 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/httpserver.h"
#include "rtc_base/gunit.h"
#include "rtc_base/testutils.h"

using namespace webrtc::testing;

namespace rtc {

namespace {
const char* const kRequest =
    "GET /index.html HTTP/1.1\r\n"
    "Host: localhost\r\n"
    "\r\n";

struct HttpServerMonitor : public sigslot::has_slots<> {
  HttpServerTransaction* transaction;
  bool server_closed, connection_closed;

  HttpServerMonitor(HttpServer* server)
      : transaction(nullptr), server_closed(false), connection_closed(false) {
    server->SignalCloseAllComplete.connect(this, &HttpServerMonitor::OnClosed);
    server->SignalHttpRequest.connect(this, &HttpServerMonitor::OnRequest);
    server->SignalHttpRequestComplete.connect(
        this, &HttpServerMonitor::OnRequestComplete);
    server->SignalConnectionClosed.connect(
        this, &HttpServerMonitor::OnConnectionClosed);
  }
  void OnRequest(HttpServer*, HttpServerTransaction* t) {
    ASSERT_FALSE(transaction);
    transaction = t;
    transaction->response.set_success();
    transaction->response.setHeader(HH_CONNECTION, "Close");
  }
  void OnRequestComplete(HttpServer*, HttpServerTransaction* t, int) {
    ASSERT_EQ(transaction, t);
    transaction = nullptr;
  }
  void OnClosed(HttpServer*) { server_closed = true; }
  void OnConnectionClosed(HttpServer*, int, StreamInterface* stream) {
    connection_closed = true;
    delete stream;
  }
};

void CreateClientConnection(HttpServer& server,
                            HttpServerMonitor& monitor,
                            bool send_request) {
  StreamSource* client = new StreamSource;
  client->SetState(SS_OPEN);
  server.HandleConnection(client);
  EXPECT_FALSE(monitor.server_closed);
  EXPECT_FALSE(monitor.transaction);

  if (send_request) {
    // Simulate a request
    client->QueueString(kRequest);
    EXPECT_FALSE(monitor.server_closed);
  }
}
}  // anonymous namespace

TEST(HttpServer, DoesNotSignalCloseUnlessCloseAllIsCalled) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Simulate a response
  ASSERT_TRUE(nullptr != monitor.transaction);
  server.Respond(monitor.transaction);
  EXPECT_FALSE(monitor.transaction);
  // Connection has closed, but no server close signal
  EXPECT_FALSE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseWhenNoConnectionsAreActive) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an idle client connection
  CreateClientConnection(server, monitor, false);
  // Perform graceful close
  server.CloseAll(false);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseAfterGracefulCloseAll) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Initiate a graceful close
  server.CloseAll(false);
  EXPECT_FALSE(monitor.server_closed);
  // Simulate a response
  ASSERT_TRUE(nullptr != monitor.transaction);
  server.Respond(monitor.transaction);
  EXPECT_FALSE(monitor.transaction);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

TEST(HttpServer, SignalsCloseAfterForcedCloseAll) {
  HttpServer server;
  HttpServerMonitor monitor(&server);
  // Add an active client connection
  CreateClientConnection(server, monitor, true);
  // Initiate a forceful close
  server.CloseAll(true);
  // Connections have all closed
  EXPECT_TRUE(monitor.server_closed);
  EXPECT_TRUE(monitor.connection_closed);
}

}  // namespace rtc
